Micron Document
Jon's Git Node

Node / forks / nomadnet / files / nomadnet / InterfaceProfiles.py

Displaying Raw • Download

nomadnet/InterfaceProfiles.py de786a0dca4eb46140ce99d1dc95a785b2f58a7e (de786a0d) Text, 8.20 KB

import os
import threading

import RNS
import RNS.vendor.umsgpack as msgpack
from nomadnet.util import write_config_atomic


class InterfaceProfiles:
VERSION = 1

def __init__(self, app):
self.app = app
self.path = os.path.join(app.storagepath, "interface_profiles.msgpack")
self._lock = threading.Lock()
self.profiles = []
self.load()

def load(self):
self.profiles = []
self.default_members = None
try:
if not os.path.isfile(self.path):
return
with open(self.path, "rb") as f:
data = msgpack.unpackb(f.read())
if not isinstance(data, dict):
return
clean = []
seen = set()
for p in data.get("profiles") or []:
if not isinstance(p, dict):
continue
pid = str(p.get("id") or "")
if not pid or pid in seen:
continue
members = [str(m) for m in (p.get("members") or []) if isinstance(m, (str, bytes))]
clean.append({"id": pid, "name": str(p.get("name") or ""), "members": members})
seen.add(pid)
self.profiles = clean
dm = data.get("default_members")
self.default_members = [str(m) for m in dm] if isinstance(dm, list) else None
except Exception as e:
RNS.log("Could not load interface profiles: "+str(e), RNS.LOG_ERROR)
self.profiles = []
self.default_members = None

def save(self):
with self._lock:
tmp = self.path + ".tmp"
try:
payload = {
"version": InterfaceProfiles.VERSION,
"profiles": [{"id": p["id"], "name": p["name"], "members": list(p["members"])} for p in self.profiles],
"default_members": self.default_members,
}
with open(tmp, "wb") as f:
f.write(msgpack.packb(payload))
f.flush()
try: os.fsync(f.fileno())
except Exception: pass
os.replace(tmp, self.path)
except Exception as e:
RNS.log("Could not save interface profiles: "+str(e), RNS.LOG_ERROR)
try: os.remove(tmp)
except Exception: pass

def _interfaces(self):
try:
return self.app.rns.config["interfaces"]
except Exception:
return {}

def interface_names(self):
try:
return list(self._interfaces().keys())
except Exception:
return []

def _is_enabled(self, iface):
return str(iface.get("enabled")).lower() not in ('false', 'off', 'no', '0') and \\
str(iface.get("interface_enabled")).lower() not in ('false', 'off', 'no', '0')

def enabled_set(self):
interfaces = self._interfaces()
result = set()
for name in interfaces:
try:
if self._is_enabled(interfaces[name]):
result.add(name)
except Exception:
pass
return result

def get(self, pid):
for p in self.profiles:
if p["id"] == pid:
return p
return None

def members_existing(self, p):
existing = set(self.interface_names())
return set(m for m in p["members"] if m in existing)

def active_profile_id(self):
enabled = self.enabled_set()
for p in self.profiles:
if self.members_existing(p) == enabled:
return p["id"]
return None

def profiles_for(self, iface_name):
return [p for p in self.profiles if iface_name in p["members"]]

def label_for(self, iface_name):
names = [p["name"] or p["id"] for p in self.profiles_for(iface_name)]
return ", ".join(names) if names else None

def member_count(self, p):
return len(self.members_existing(p))

def _new_id(self):
while True:
pid = os.urandom(4).hex()
if not self.get(pid):
return pid

def create(self, name):
pid = self._new_id()
self.profiles.append({"id": pid, "name": name or "Profile", "members": []})
self.save()
return pid

def save_current_as_profile(self, name):
pid = self.create(name)
self.set_members(pid, sorted(self.enabled_set()))
return pid

def rename(self, pid, name):
p = self.get(pid)
if p is not None and name:
p["name"] = name
self.save()

def delete(self, pid):
self.profiles = [p for p in self.profiles if p["id"] != pid]
self.save()

def set_members(self, pid, members):
p = self.get(pid)
if p is not None:
valid = set(self.interface_names())
seen = set()
ordered = []
for m in members:
if m in valid and m not in seen:
ordered.append(m)
seen.add(m)
p["members"] = ordered
self.save()

def set_interface_profiles(self, iface_name, profile_ids):
pset = set(profile_ids)
for p in self.profiles:
if p["id"] in pset:
if iface_name not in p["members"]:
p["members"].append(iface_name)
elif iface_name in p["members"]:
p["members"] = [m for m in p["members"] if m != iface_name]
self.save()

def _apply_members(self, members):
existing = set(self.interface_names())
members = set(m for m in members if m in existing)
interfaces = self._interfaces()
try:
for name in interfaces:
value = "true" if name in members else "false"
interfaces[name]["interface_enabled"] = value
if "enabled" in interfaces[name]:
interfaces[name]["enabled"] = value
write_config_atomic(self.app.rns.config)
return True
except Exception as e:
RNS.log("Could not apply interface profile: "+str(e), RNS.LOG_ERROR)
return False

def select_profile(self, pid):
p = self.get(pid)
if p is None:
return False
return self._apply_members(self.members_existing(p))

def select_default(self):
if self.default_members is None:
return False
return self._apply_members(set(self.default_members))

def update_default_if_custom(self):
# Remember the live enabled-set as "Default" whenever it matches no named
# profile -- i.e. it's the user's manual setup. Only relevant once profiles exist.
if not self.profiles:
return
enabled = self.enabled_set()
matches_named = any(self.members_existing(p) == enabled for p in self.profiles)
if matches_named and self.default_members is not None:
return
new_default = sorted(enabled)
if self.default_members != new_default:
self.default_members = new_default
self.save()

def remove_interface(self, iface_name):
changed = False
for p in self.profiles:
if iface_name in p["members"]:
p["members"] = [m for m in p["members"] if m != iface_name]
changed = True
if changed:
self.save()

def rename_interface(self, old_name, new_name):
changed = False
for p in self.profiles:
if old_name in p["members"]:
p["members"] = [new_name if m == old_name else m for m in p["members"]]
changed = True
if changed:
self.save()

def prune(self):
valid = set(self.interface_names())
changed = False
for p in self.profiles:
pruned = [m for m in p["members"] if m in valid]
if len(pruned) != len(p["members"]):
p["members"] = pruned
changed = True
if changed:
self.save()

Served by rngit 1.5.4 - Generated in 0.03s